package com.cjt2325.cameralibrary;

import static ohos.media.camera.device.Camera.FrameConfigType.FRAME_CONFIG_PICTURE;

import com.cjt2325.cameralibrary.constant.Constants;
import com.cjt2325.cameralibrary.listener.CameraStateCallbackImpl;
import com.cjt2325.cameralibrary.listener.ErrorListener;
import com.cjt2325.cameralibrary.state.CameraMachine;

import com.cjt2325.cameralibrary.util.CameraParamUtil;
import com.cjt2325.cameralibrary.util.LogUtil;
import com.cjt2325.cameralibrary.util.DeviceUtil;
import com.cjt2325.cameralibrary.util.FileUtil;
import com.cjt2325.cameralibrary.util.ScreenUtils;
import com.cjt2325.cameralibrary.util.AngleUtil;

import ohos.agp.animation.AnimatorGroup;
import ohos.agp.animation.AnimatorProperty;
import ohos.agp.components.Image;
import ohos.agp.graphics.Surface;
import ohos.agp.graphics.SurfaceOps;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.media.camera.CameraKit;

import ohos.media.camera.device.FrameConfig;
import ohos.media.camera.device.Camera;
import ohos.media.camera.device.CameraConfig;
import ohos.media.camera.device.CameraInfo;

import ohos.media.common.AudioProperty;
import ohos.media.common.Source;
import ohos.media.common.StorageProperty;
import ohos.media.common.VideoProperty;
import ohos.media.image.ImageReceiver;
import ohos.media.image.common.ImageFormat;
import ohos.media.image.common.Size;
import ohos.media.recorder.Recorder;
import ohos.sensor.agent.CategoryMotionAgent;
import ohos.sensor.bean.CategoryMotion;
import ohos.sensor.data.CategoryMotionData;
import ohos.sensor.listener.ICategoryMotionDataCallback;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Optional;

/**
 * Camera Interface
 */
public class CameraInterface {
    /**
     * TYPE RECORDER
     */
    public static final int TYPE_RECORDER = 0x090;

    /**
     * TYPE RECORDER
     */
    public static final int TYPE_CAPTURE = 0x091;

    private static final String TAG = "CameraView";

    private static final long INTERVAL = 100000000;

    private static volatile CameraInterface sCameraInterface;

    private EventHandler mCameraHandler = new EventHandler(EventRunner.create("CameraThread"));

    // 视频质量
    private int mMediaQuality = JCameraView.MEDIA_QUALITY_MIDDLE;

    private CameraKit mCameraKit;
    private String mCameraId;
    private CameraStateCallbackImpl mCameraStateCallback;

    private FrameConfig.Builder mParams;
    private boolean isPreviewing = false;

    private int SELECTED_CAMERA = -1;
    private int CAMERA_BACK_POSITION = 0;
    private int CAMERA_FRONT_POSITION = 1;

    private float mScreenProp = -1.0f;

    private boolean isRecorder = false;
    private Recorder mMediaRecorder;
    private String mVideoFileName;
    private String mSaveVideoPath;
    private String mVideoFileAbsPath;

    private ErrorListener mErrorListener;

    private Image mSwitchView;
    private Image mFlashLamp;

    private int mAngle = 0;
    private int mCameraAngle = 90; // 摄像头角度   默认为90度
    private int mRotation = 0;

    private int mNowScaleRate = 0;
    private int mRecordScaleRate = 0;

    private CategoryMotionAgent mSm = null;

    /**
     * 获取CameraInterface单例
     *
     * @return CameraInterface
     */
    public static synchronized CameraInterface getInstance() {
        if (sCameraInterface == null) {
            synchronized (CameraInterface.class) {
                if (sCameraInterface == null) {
                    sCameraInterface = new CameraInterface();
                }
            }
        }
        return sCameraInterface;
    }

    /**
     * set SwitchView
     *
     * @param switchView Image
     * @param mFlashLamp Image
     */
    public void setSwitchView(Image switchView, Image mFlashLamp) {
        this.mSwitchView = switchView;
        this.mFlashLamp = mFlashLamp;
        if (switchView != null && mCameraStateCallback != null && mCameraStateCallback.getCamera() != null) {
            FrameConfig.Builder builder =
                    mCameraStateCallback.getCamera().getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW);
            if (builder != null) {
                int or = builder.getImageRotation();
                mCameraAngle =
                        CameraParamUtil.getInstance()
                                .getCameraDisplayOrientation(
                                        mSwitchView.getContext(),
                                        mCameraId == null ? 0 : Integer.parseInt(mCameraId),
                                        or);
            }
        }
    }

    private ICategoryMotionDataCallback sensorEventListener =
            new ICategoryMotionDataCallback() {
                @Override
                public void onSensorDataModified(CategoryMotionData event) {
                    if (event.sensor.getFlags() != CategoryMotion.SENSOR_TYPE_ACCELEROMETER) {
                        return;
                    }
                    float[] values = event.values;
                    mAngle = AngleUtil.getSensorAngle(values[0], values[1]);
                    rotationAnimation();
                }

                @Override
                public void onAccuracyDataModified(CategoryMotion categoryMotion, int i) {
                }

                @Override
                public void onCommandCompleted(CategoryMotion categoryMotion) {
                }
            };

    /**
     * 切换摄像头icon跟随手机角度进行旋转
     */
    private void rotationAnimation() {
        if (mRotation != mAngle) {
            int[] rotation = getRotation();
            startRotation(rotation[0], rotation[1]);
            mRotation = mAngle;
        }
    }

    private int[] getRotation() {
        int[] rotations = new int[2];
        switch (mRotation) {
            case 0:
                rotations[0] = 0;
                switch (mAngle) {
                    case 90:
                        rotations[1] = -90;
                        break;
                    case 270:
                        rotations[1] = 90;
                        break;
                }
                break;
            case 90:
                rotations[0] = -90;
                switch (mAngle) {
                    case 0:
                        rotations[1] = 0;
                        break;
                    case 180:
                        rotations[1] = -180;
                        break;
                }
                break;
            case 180:
                rotations[0] = 180;
                switch (mAngle) {
                    case 90:
                        rotations[1] = 270;
                        break;
                    case 270:
                        rotations[1] = 90;
                        break;
                }
                break;
            case 270:
                rotations[0] = 90;
                switch (mAngle) {
                    case 0:
                        rotations[1] = 0;
                        break;
                    case 180:
                        rotations[1] = 180;
                        break;
                }
                break;
        }
        return rotations;
    }

    private void startRotation(int startRotation, int endRotation) {
        if (mSwitchView == null) {
            return;
        }
        AnimatorProperty switchAnimation = new AnimatorProperty(mSwitchView);
        switchAnimation.rotate(startRotation).rotate(endRotation);

        AnimatorProperty flashAnimation = new AnimatorProperty(mFlashLamp);
        flashAnimation.rotate(startRotation).rotate(endRotation);

        AnimatorGroup animatorGroup = new AnimatorGroup();
        animatorGroup.runParallel(switchAnimation, flashAnimation);
        animatorGroup.setDuration(500);
        animatorGroup.start();
    }

    void setSaveVideoPath(String saveVideoPath) {
        this.mSaveVideoPath = saveVideoPath;
        File file = new File(saveVideoPath);
        if (!file.exists()) {
            boolean isSuccess = file.mkdirs();
            if (!isSuccess) {
                LogUtil.error(LogUtil.DEFAULT_TAG, "file create fail");
            }
        }
    }

    /**
     * set Zoom
     *
     * @param zoom float
     * @param type int
     */
    public void setZoom(float zoom, int type) {
        LogUtil.error(LogUtil.DEFAULT_TAG, "set zoom:" + zoom + ";type:" + type);
        if (mCameraStateCallback == null || mCameraStateCallback.getCamera() == null) {
            return;
        }
        if (mParams == null) {
            mParams =
                    mCameraStateCallback.getCamera().getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW);
        }
        switch (type) {
            case TYPE_RECORDER:
                // 如果不是录制视频中，上滑不会缩放
                if (!isRecorder) {
                    return;
                }
                if (zoom >= 0) {
                    // 每移动50个像素缩放一个级别
                    int scaleRate = (int) (zoom / 40);
                    if (scaleRate <= mParams.getZoomValue()
                            && scaleRate >= mNowScaleRate
                            && mRecordScaleRate != scaleRate) {
                        setZoom(scaleRate);
                        mRecordScaleRate = scaleRate;
                    }
                }
                break;
            case TYPE_CAPTURE:
                if (isRecorder) {
                    return;
                }
                // 每移动50个像素缩放一个级别
                int scaleRate = (int) (zoom / 50);
                if (scaleRate < mParams.getZoomValue()) {
                    mNowScaleRate += scaleRate;
                    if (mNowScaleRate < 0) {
                        mNowScaleRate = 0;
                    } else if (mNowScaleRate > mParams.getZoomValue()) {
                        mNowScaleRate = (int) mParams.getZoomValue();
                    } else {
                        LogUtil.info(TAG, "mNowScaleRate = " + mNowScaleRate);
                    }
                    setZoom(mNowScaleRate);
                }
                LogUtil.info(TAG, "setZoom = " + mNowScaleRate);
                break;
        }
    }

    void setMediaQuality(int quality) {
        this.mMediaQuality = quality;
    }

    private void setZoom(float zoom) {
        LogUtil.error(LogUtil.DEFAULT_TAG, "set zoom:" + zoom);
        if (mCameraStateCallback == null || mCameraStateCallback.getCamera() == null) {
            return;
        }
        FrameConfig.Builder builder =
                mCameraStateCallback.getCamera().getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW);
        builder.setZoom(zoom);
        mCameraStateCallback.getCamera().triggerLoopingCapture(builder.build());
    }

    /**
     * set FlashMode
     *
     * @param flashMode int
     */
    public void setFlashMode(int flashMode) {
        if (mCameraStateCallback == null || mCameraStateCallback.getCamera() == null) {
            return;
        }
        FrameConfig.Builder builder =
                mCameraStateCallback.getCamera().getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW);
        builder.addSurface(
                mCameraStateCallback.getMachine().getView().getSurfaceProvider().getSurfaceOps().get().getSurface());
        builder.setFlashMode(flashMode);
        mCameraStateCallback.getCamera().triggerLoopingCapture(builder.build());
    }

    /**
     * Camera OpenOver Callback
     */
    public interface CameraOpenOverCallback {
        void cameraHasOpened();
    }

    private CameraInterface() {
        findAvailableCameras();
        SELECTED_CAMERA = CAMERA_BACK_POSITION;
        mSaveVideoPath = "";
    }

    /**
     * re Open Camera
     *
     * @param machine CameraMachine
     */
    public void reOpenCamera(CameraMachine machine) {
        if (mCameraStateCallback != null && mCameraStateCallback.getCamera() != null) {
            mCameraStateCallback.getCamera().release();
        }
        mCameraStateCallback = null;
        openCamera(machine);
    }

    /**
     * open Camera
     *
     * @param callback CameraOpenOverCallback
     * @param machine  CameraMachine
     */
    void doOpenCamera(CameraOpenOverCallback callback, CameraMachine machine) {
        if (mCameraStateCallback == null || mCameraStateCallback.getCamera() == null) {
            openCamera(machine);
        }
        callback.cameraHasOpened();
    }

    private void openCamera(CameraMachine machine) {
        LogUtil.error(LogUtil.DEFAULT_TAG, "openCamera");
        if (mCameraStateCallback == null) {
            mCameraStateCallback = new CameraStateCallbackImpl(machine);
        }
        try {
            mCameraKit = CameraKit.getInstance(machine.getContext().getApplicationContext());
            if (mCameraKit == null) {
                if (mErrorListener != null) {
                    mErrorListener.onError("CameraKit Invalid", Constants.ERROR_TYPE_PICTURE);
                }
                LogUtil.error(TAG, "there is no camera");
                return;
            }
            Optional<SurfaceOps> surfaceOps = machine.getView().getSurfaceProvider().getSurfaceOps();
            SurfaceOps ops = null;
            if (surfaceOps.isPresent()) {
                ops = surfaceOps.get();
            }
            changeCamera(SELECTED_CAMERA, ops);

        } catch (Exception var3) {
            if (this.mErrorListener != null) {
                this.mErrorListener.onError("openCamera exception:" + var3.getMessage(), Constants.ERROR_TYPE_PICTURE);
            }
        }
        LogUtil.error(LogUtil.DEFAULT_TAG, "openCamera end");
    }

    /**
     * 相机前后切换
     *
     * @param cameraTag  int
     * @param surfaceOps SurfaceOps
     */
    private void changeCamera(int cameraTag, SurfaceOps surfaceOps) {
        String[] cameraIds = mCameraKit.getCameraIds();
        if (cameraIds == null || cameraIds.length <= 0) {
            if (mErrorListener != null) {
                mErrorListener.onError("getCameraIds Invalid", Constants.ERROR_TYPE_PICTURE);
            }
            LogUtil.error(TAG, "cameraIds size is 0");
            return;
        }
        boolean isBack = cameraTag == CAMERA_BACK_POSITION;
        mCameraId = cameraIds.length > 1 && isBack ? cameraIds[0] : cameraIds[1];
        if (surfaceOps != null) {
            int screenWidth = ScreenUtils.getScreenWidth(mCameraStateCallback.getMachine().getContext());
            int screenHeight = ScreenUtils.getScreenHeight(mCameraStateCallback.getMachine().getContext());
            Size size = getOptimalSize(mCameraKit, mCameraId, screenWidth, screenHeight);
            if (size != null) {
                surfaceOps.setFixedSize(size.width, size.height);
            }
        }
        mCameraKit.createCamera(mCameraId, mCameraStateCallback, mCameraHandler);
        LogUtil.error(LogUtil.DEFAULT_TAG, "open camera success");
    }

    /**
     * getOptimalSize
     *
     * @param cameraKit    CameraKit
     * @param cameraId     String
     * @param screenWidth  int
     * @param screenHeight int
     * @return Size
     */
    private Size getOptimalSize(CameraKit cameraKit, String cameraId, int screenWidth, int screenHeight) {
        List<Size> sizes = cameraKit.getCameraAbility(cameraId).getSupportedSizes(ImageFormat.YUV420_888);
        final double ASPECT_TOLERANCE = 0.1;
        //竖屏screenHeight/screenWidth    横屏 screenWidth/screenHeight
        double targetRatio = (double) screenHeight / screenWidth;
        Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;
        int target = screenWidth;
        for (Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) {
                continue;
            }
            if (Math.abs(size.height - target) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - target);
            }
        }
        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Size size : sizes) {
                if (Math.abs(size.height - target) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - target);
                }
            }
        }
        return optimalSize;
    }

    /**
     * switch Camera
     *
     * @param holder     SurfaceOps
     * @param screenProp screenProp
     * @param machine    CameraMachine
     */
    public synchronized void switchCamera(SurfaceOps holder, float screenProp, CameraMachine machine) {
        if (SELECTED_CAMERA == CAMERA_BACK_POSITION) {
            SELECTED_CAMERA = CAMERA_FRONT_POSITION;
        } else {
            SELECTED_CAMERA = CAMERA_BACK_POSITION;
        }
        doDestroyCamera();
        openCamera(machine);
        doStartPreview(holder, screenProp);
    }

    /**
     * doStartPreview
     *
     * @param holder     SurfaceOps
     * @param screenProp float
     */
    public void doStartPreview(SurfaceOps holder, float screenProp) {
        isPreviewing = true;
        if (this.mScreenProp < 0) {
            this.mScreenProp = screenProp;
        }
    }

    /**
     * 停止预览
     */
    public void doStopPreview() {
        if (mCameraStateCallback != null && mCameraStateCallback.getCamera() != null) {
            mCameraStateCallback.getCamera().stopLoopingCapture();
            isPreviewing = false;
            LogUtil.info(TAG, "=== Stop Preview ===");
        }
    }

    /**
     * 销毁Camera
     */
    void doDestroyCamera() {
        mErrorListener = null;
        if (mCameraStateCallback != null && mCameraStateCallback.getCamera() != null) {
            mSwitchView = null;
            mFlashLamp = null;
            mCameraStateCallback.getCamera().stopLoopingCapture();
            isPreviewing = false;
            mCameraStateCallback.getCamera().release();
            mCameraStateCallback = null;
            LogUtil.info(TAG, "=== Destroy Camera ===");
        } else {
            LogUtil.info(TAG, "=== Camera  Null===");
        }
    }

    /**
     * 拍照
     */
    public void takePicture() {
        LogUtil.info(LogUtil.DEFAULT_TAG, "takePicture");
        if (mCameraStateCallback == null) {
            return;
        }
        Camera camera = mCameraStateCallback.getCamera();

        ImageReceiver imageReceiver = mCameraStateCallback.getMachine().getView().getImageReceiver();
        FrameConfig.Builder framePictureConfigBuilder = camera.getFrameConfigBuilder(FRAME_CONFIG_PICTURE);

        framePictureConfigBuilder.addSurface(imageReceiver.getRecevingSurface());
        framePictureConfigBuilder.setImageRotation(90);
        FrameConfig pictureFrameConfig = framePictureConfigBuilder.build();
        camera.triggerSingleCapture(pictureFrameConfig);
        LogUtil.info(LogUtil.DEFAULT_TAG, "takePicture end");
    }

    /**
     * 启动录像
     */
    public void startRecord() {
        if (isRecorder) {
            return;
        }
        if (mCameraKit == null) {
            return;
        }
        initMediaRecorder();
        mCameraStateCallback.setRecorder(mMediaRecorder);

        mMediaRecorder.getVideoSurface().syncCanvasDrawCalls();

        Surface videoSurface = mMediaRecorder.getVideoSurface();
        isRecorder = true;

        CameraConfig.Builder cameraConfigBuilder = mCameraStateCallback.getCamera().getCameraConfigBuilder();
        if (videoSurface != null) {
            cameraConfigBuilder.addSurface(videoSurface);
        }
        cameraConfigBuilder.addSurface(
                mCameraStateCallback.getMachine().getView().getSurfaceProvider().getSurfaceOps().get().getSurface());
        mCameraStateCallback.getCamera().configure(cameraConfigBuilder.build());

        LogUtil.error(LogUtil.DEFAULT_TAG, "startRecord end");
    }

    private void initMediaRecorder() {
        if (mMediaRecorder == null) {
            mMediaRecorder = new Recorder();
        }
        mMediaRecorder.reset();
        int nowAngle = (mAngle + 90) % 360;

        Source source = new Source();
        source.setRecorderAudioSource(Recorder.AudioSource.MIC);
        source.setRecorderVideoSource(Recorder.VideoSource.SURFACE);
        mMediaRecorder.setSource(source);
        mMediaRecorder.setOutputFormat(Recorder.OutputFormat.MPEG_4);

        VideoProperty.Builder videoPropertyBuilder = new VideoProperty.Builder();

        setRecorderDegrees(nowAngle, videoPropertyBuilder);

        if (DeviceUtil.isHuaWeiRongyao()) {
            videoPropertyBuilder.setRecorderBitRate(4 * 100000);
        } else {
            videoPropertyBuilder.setRecorderBitRate(mMediaQuality);
        }

        videoPropertyBuilder.setRecorderFps(Constants.RECORDER_FPS);

        setRecorderSize(videoPropertyBuilder);

        videoPropertyBuilder.setRecorderVideoEncoder(Recorder.VideoEncoder.H264);
        videoPropertyBuilder.setRecorderRate(Constants.RECORDER_RATE);
        mMediaRecorder.setVideoProperty(videoPropertyBuilder.build());

        mVideoFileName = "video_" + System.currentTimeMillis() + ".mp4";
        if ("".equals(mSaveVideoPath)) {
            mSaveVideoPath = FileUtil.sCacheFilePath;
        }
        mVideoFileAbsPath = mSaveVideoPath + File.separator + mVideoFileName;
        StorageProperty storageProperty = new StorageProperty.Builder().setRecorderPath(mVideoFileAbsPath).build();
        mMediaRecorder.setStorageProperty(storageProperty);

        AudioProperty.Builder audioPropertyBuilder = new AudioProperty.Builder();
        audioPropertyBuilder.setRecorderAudioEncoder(Recorder.AudioEncoder.AAC);
        mMediaRecorder.setAudioProperty(audioPropertyBuilder.build());
        mMediaRecorder.prepare();

        LogUtil.error(LogUtil.DEFAULT_TAG, "initMediaRecorder success");
    }

    private void setRecorderSize(VideoProperty.Builder videoPropertyBuilder) {
        Size videoSize;
        List<Size> supportSize = mCameraKit.getCameraAbility(mCameraId).getSupportedSizes(Recorder.class);
        if (supportSize == null || supportSize.size() == 0) {
            videoSize =
                    CameraParamUtil.getInstance()
                            .getPreviewSize(
                                    mCameraKit.getCameraAbility(mCameraId).getSupportedSizes(ImageFormat.JPEG),
                                    600,
                                    mScreenProp);
        } else {
            videoSize = CameraParamUtil.getInstance().getPreviewSize(supportSize, 600, mScreenProp);
        }

        videoPropertyBuilder.setRecorderWidth(Math.max(videoSize.width, videoSize.height));
        videoPropertyBuilder.setRecorderHeight(Math.min(videoSize.width, videoSize.height));
    }

    private void setRecorderDegrees(int nowAngle, VideoProperty.Builder videoPropertyBuilder) {
        if (SELECTED_CAMERA == CAMERA_FRONT_POSITION) {
            // 手机预览倒立的处理
            if (mCameraAngle == 270) {
                // 横屏
                if (nowAngle == 0) {
                    videoPropertyBuilder.setRecorderDegrees(180);
                } else if (nowAngle == 270) {
                    videoPropertyBuilder.setRecorderDegrees(270);
                } else {
                    videoPropertyBuilder.setRecorderDegrees(90);
                }
            } else {
                if (nowAngle == 90) {
                    videoPropertyBuilder.setRecorderDegrees(270);
                } else if (nowAngle == 270) {
                    videoPropertyBuilder.setRecorderDegrees(90);
                } else {
                    videoPropertyBuilder.setRecorderDegrees(nowAngle);
                }
            }
        } else {
            videoPropertyBuilder.setRecorderDegrees(nowAngle);
        }
    }

    /**
     * 停止录像
     *
     * @param isShort  boolean
     * @param callback StopRecordCallbacks
     */
    public void stopRecord(boolean isShort, StopRecordCallback callback) {
        LogUtil.error(LogUtil.DEFAULT_TAG, "stopRecord");
        if (!isRecorder) {
            return;
        }
        Surface surface = null;
        if (mMediaRecorder != null) {
            surface = mMediaRecorder.getVideoSurface();
            try {
                mMediaRecorder.stop();
            } catch (RuntimeException e) {
                mMediaRecorder = null;
                mMediaRecorder = new Recorder();
            } finally {
                if (mMediaRecorder != null) {
                    mMediaRecorder.release();
                }
                mMediaRecorder = null;
                isRecorder = false;
            }
            if (isShort) {
                if (FileUtil.deleteFile(mVideoFileAbsPath)) {
                    callback.recordResult(null);
                }
                return;
            }
            doStopPreview();

            mCameraStateCallback.setRecorder(null);
            CameraConfig.Builder cameraConfigBuilder = mCameraStateCallback.getCamera().getCameraConfigBuilder();
            cameraConfigBuilder.addSurface(
                    mCameraStateCallback
                            .getMachine()
                            .getView()
                            .getSurfaceProvider()
                            .getSurfaceOps()
                            .get()
                            .getSurface());

            if (surface != null) {
                cameraConfigBuilder.removeSurface(surface);
                surface = null;
            }
            mCameraStateCallback.getCamera().configure(cameraConfigBuilder.build());

            String fileName = mSaveVideoPath + File.separator + mVideoFileName;
            callback.recordResult(fileName);
        }
    }

    private void findAvailableCameras() {
        if (mCameraKit == null) {
            return;
        }
        String[] cameraIds = mCameraKit.getCameraIds();
        if (cameraIds == null || cameraIds.length == 0) {
            return;
        }
        for (String cameraId : cameraIds) {
            CameraInfo info = mCameraKit.getCameraInfo(cameraId);
            switch (info.getFacingType()) {
                case CameraInfo.FacingType.CAMERA_FACING_FRONT:
                    CAMERA_FRONT_POSITION = info.getFacingType();
                    break;
                case CameraInfo.FacingType.CAMERA_FACING_BACK:
                    CAMERA_BACK_POSITION = info.getFacingType();
                    break;
                default:
                    break;
            }
        }
    }

    private static int clamp(int xx, int min, int max) {
        if (xx > max) {
            return max;
        }
        if (xx < min) {
            return min;
        }
        return xx;
    }

    void setErrorListener(ErrorListener errorListener) {
        this.mErrorListener = errorListener;
    }

    /**
     * Stop Record Callback
     */
    public interface StopRecordCallback {
        void recordResult(String url);
    }

    /**
     * Focus Callback
     */
    public interface FocusCallback {
        void focusSuccess();
    }

    void registerSensorManager() {
        if (mSm == null) {
            mSm = new CategoryMotionAgent();
        }
        CategoryMotion categoryMotion = mSm.getSingleSensor(CategoryMotion.SENSOR_TYPE_ACCELEROMETER);
        mSm.setSensorDataCallback(sensorEventListener, categoryMotion, INTERVAL);
    }

    void unregisterSensorManager() {
        if (mSm != null) {
            mSm.releaseSensorDataCallback(sensorEventListener);
        }
    }

    void isPreview(boolean res) {
        this.isPreviewing = res;
    }
}
